# coding:utf-8

import calendar
import datetime
import json
from old_version.src.util.TimeUtil import TimeUtil
from old_version.src.util.RedisUtil import RedisUtil
from old_version.src.util.ListUtil import ListUtil
from old_version.src.util.NumUtil import NumUtil
from old_version.src import StockWeekend
from old_version.src import StockRecord
from collections import deque

'''
StockWeekend的服务类
'''


class StockWeekendService:

    def __init__(self):
        pass

    '''
    某个日期之后的下一个星期一的日期
    '''
    def get_next_monday(self, date):
        year = int(date[0:4])
        month = int(date[4:6])
        day = int(date[6:8])
        date_object = datetime.date(year, month, day)
        temp_date_object = datetime.date(year, month, day)
        one_day = datetime.timedelta(days=1)
        m1 = calendar.MONDAY
        # 添加条件date_object == temp_date_object是为了避免当前日期正好是星期一
        while date_object.weekday() != m1 or date_object == temp_date_object:
            date_object += one_day
        next_monday = date_object.strftime('%Y%m%d')
        return next_monday

    '''
    计算股票周线级别的基本数据
    '''
    def calculate_stock_weekend(self):
        print(TimeUtil.date(), "calculate_stock_record_weekend method start")
        # 用于存储最后所有的StockWeekend对象，然后存储到redis中
        result_stock_weekend_list = list()
        # 获取所有StockRecord开头的key
        all_row_keys = RedisUtil.redis_conn.keys("StockRecord_*")
        if all_row_keys is not None and len(all_row_keys)>0:
            for row_key in all_row_keys:
                # 获取某只股票的所有StockRecord记录，并以升序排列
                stock_record_list = RedisUtil.redis_conn.zrange(row_key, 0, -1, withscores=True)
                # 获取某只股票第一个交易日之后的第一个星期一的日期
                first_next_monday = TimeUtil.to_date(self.get_next_monday(str(int(stock_record_list[0][1]))))
                # 用于存储一个星期中所有的StockRecord记录
                stock_weekend_list = list()
                # 表示周线级别某一只股票的开盘价、收盘价、最高价、最低价、总成交量、总成交额
                stock_weekend_open = float()
                stock_weekend_close = float()
                stock_weekend_high = float()
                stock_weekend_low = float()
                stock_weekend_volume = float()
                stock_weekend_amount = float()
                for stock_record_tuple in stock_record_list:
                    # 转换为date对象类型
                    str_date = str(int(stock_record_tuple[1]))
                    stock_record_date = TimeUtil.to_date(str_date)
                    if stock_record_date < first_next_monday:
                        # 如果是属于同一个星期的记录就保存到数组中
                        stock_weekend_list.append(stock_record_tuple[0])
                    # 当stock_weekend_list的长度为6时，则第6个元素是下个星期的第一个交易日
                    if stock_record_date >= first_next_monday or len(stock_weekend_list) == 6:
                        # 计算一个星期中的开盘价，收盘价，最高价和最低价
                        stock_weekend_open = StockWeekend.to_entity(stock_weekend_list[0]).open
                        stock_weekend_close = StockWeekend.to_entity(stock_weekend_list[len(stock_weekend_list)-1]).close
                        if stock_weekend_open > stock_weekend_close:
                            stock_weekend_high = stock_weekend_open
                            stock_weekend_low = stock_weekend_close
                        if stock_weekend_open < stock_weekend_close:
                            stock_weekend_high = stock_weekend_close
                            stock_weekend_low = stock_weekend_open
                        for stock_weekend in stock_weekend_list:
                            stock_weekend_obj = StockWeekend.to_entity(stock_weekend)
                            if stock_weekend_obj.high > stock_weekend_high:
                                stock_weekend_high = stock_weekend_obj.high
                            if stock_weekend_obj.low < stock_weekend_low:
                                stock_weekend_low = stock_weekend_obj.low
                        # 这个星期这只股票的总成交量和总成交额
                        for stock_weekend in stock_weekend_list:
                            stock_weekend_obj = StockWeekend.to_entity(stock_weekend)
                            stock_weekend_volume = stock_weekend_volume + float(stock_weekend_obj.volume)
                            stock_weekend_amount = stock_weekend_amount + float(stock_weekend_obj.amount)
                        # 创建StockWeekend对象
                        code = StockWeekend.to_entity(stock_weekend_list[0]).code
                        begin_date = StockRecord.to_entity(stock_weekend_list[0]).date
                        end_date = StockRecord.to_entity(stock_weekend_list[len(stock_weekend_list)-1]).date
                        stock_weekend = StockWeekend()
                        stock_weekend.id = str(code+"_"+begin_date+"_"+end_date)
                        stock_weekend.begin_date = str(begin_date)
                        stock_weekend.end_date = str(end_date)
                        stock_weekend.code = str(code)
                        stock_weekend.open = str(stock_weekend_open)
                        stock_weekend.close = str(stock_weekend_close)
                        stock_weekend.high = str(stock_weekend_high)
                        stock_weekend.low = str(stock_weekend_low)
                        stock_weekend.amount = str(stock_weekend_amount)
                        stock_weekend.volume = str(stock_weekend_volume)
                        # 将StockWeekend对象存储在list中，以便最后插入到redis中
                        result_stock_weekend_list.append(stock_weekend)
                        # 如果没有下面两行代码，则就没有星期一的数据了
                        stock_weekend_list = list()
                        stock_weekend_list.append(stock_record_tuple[0])
                        # 周线级别的成交量和成交额需要重新置零
                        stock_weekend_volume = float()
                        stock_weekend_amount = float()
                        # 计算first_next_monday所在星期的的下一个星期的星期一
                        first_next_monday = TimeUtil.to_date(self.get_next_monday(TimeUtil.to_str(first_next_monday)))
            # 将最后计算出来的所有的StockWeekend存储到redis中
            RedisUtil.put_stock_weekend_list(result_stock_weekend_list)

    '''
    计算周线级别KD指标
    '''
    def calculate_stock_weekend_kd(self):
        print(TimeUtil.date(), "calculate_stock_weekend_kd method start")
        # 用于存储最后所有的StockWeekend对象，然后存储到redis中
        result_stock_weekend_list = list()
        # 用于临时存储k、d指标
        temp_k = float()
        temp_d = float()
        # 获取所有StockWeekend开头的key
        all_row_keys = RedisUtil.redis_conn.keys("StockWeekend_*")
        if all_row_keys is not None and len(all_row_keys) > 0:
            for row_key in all_row_keys:
                # 获取某只股票周线级别的记录，并按升序排列
                stock_weekend_list = RedisUtil.redis_conn.zrange(row_key, 0, -1, withscores=True)
                # 计算指标时使用，用于表示日期的累积
                num = 0
                # 存储最大值和最小值的队列
                high_queue = deque([])
                low_queue = deque([])
                for stock_weekend in stock_weekend_list:
                    num += 1
                    stock_weekend_obj = StockWeekend.to_entity(stock_weekend[0])
                    high_queue.append(float(stock_weekend_obj.high.encode("utf-8")))
                    low_queue.append(float(stock_weekend_obj.low.encode("utf-8")))
                    # 初始化每只股票第一个交易周的K和D字段
                    if num == 8:
                        # 若无前一日K 值与D值，则可分别用50来代替
                        stock_weekend_obj.k = float(50)
                        stock_weekend_obj.d = float(50)
                        temp_k = stock_weekend_obj.k
                        temp_d = stock_weekend_obj.d
                    if num >= 9:
                        max_value = NumUtil.max_value_with_list(ListUtil.deque_to_list(high_queue))
                        min_value = NumUtil.min_value_with_list(ListUtil.deque_to_list(high_queue))
                        if float(max_value) == float(min_value):
                            print(stock_weekend[0])
                        stock_weekend_obj.rsv = (float(stock_weekend_obj.close) - float(min_value)) / (float(max_value) - float(min_value)) * 100
                        stock_weekend_obj.k = float(2) / float(3) * float(temp_k) + float(1) / float(3) * float(stock_weekend_obj.rsv)
                        stock_weekend_obj.d = float(2) / float(3) * float(temp_d) + float(1) / float(3) * float(stock_weekend_obj.k)
                        if high_queue.__len__() > 0:
                            high_queue.popleft()
                        if low_queue.__len__() > 0:
                            low_queue.popleft()
                        new_json_str = json.dumps(stock_weekend_obj.__dict__, encoding='UTF-8',
                                                  ensure_ascii=False).encode("utf-8")
                        # 经过json.dumps和json.loads方法后的json字符串虽然内容一样，但是顺序不一样了
                        old_json_str = stock_weekend[0]
                        # 为下一次迭代计算k、d指标做准备
                        temp_k = stock_weekend_obj.k
                        temp_d = stock_weekend_obj.d
                        # 修改redis中的记录，添加rsv、k和d指标
                        RedisUtil.redis_conn.zrem("StockWeekend_" + stock_weekend_obj.code.encode("utf-8"),
                                                  old_json_str)
                        RedisUtil.redis_conn.zadd("StockWeekend_" + stock_weekend_obj.code.encode("utf-8"),
                                                  stock_weekend_obj.end_date.encode("utf-8"), new_json_str)
                        result_stock_weekend_list.append(stock_weekend_obj)
            # 将最后计算出来的所有的StockWeekend存储到redis中
            RedisUtil.put_stock_weekend_list(result_stock_weekend_list)


if __name__ == "__main__":
    stock_weekend_service = StockWeekendService()
    # print(stock_weekend_service.get_next_monday("20180507"))
    # stock_weekend_service.calculate_stock_weekend()
    stock_weekend_service.calculate_stock_weekend_kd()